import numpy as np


# 初始化数据，包括字段和标签
def loadDataSet():
    postingList = [['my', 'dog', 'has', 'flea', 'problems', 'help', 'please'],  # [0,0,1,1,1......]
                   ['maybe', 'not', 'take', 'him', 'to', 'dog', 'park', 'stupid'],
                   ['my', 'dalmation', 'is', 'so', 'cute', 'I', 'love', 'him'],
                   ['stop', 'posting', 'stupid', 'worthless', 'garbage'],
                   ['mr', 'licks', 'ate', 'my', 'steak', 'how', 'to', 'stop', 'him'],
                   ['quit', 'buying', 'worthless', 'dog', 'food', 'stupid']]
    classVec = [0, 1, 0, 1, 0, 1]  # 1 is abusive, 0 not
    return postingList, classVec


# 创建字典，把所有字段装入一个set中，set允许有重复的字段
# 可能会是因为set不好操作，所以再把set转成list
def createDict(postingList):
    dictSet = set([])
    for line in postingList:
        dictSet = dictSet | set(line)
        # for word in line:
        #     dictSet.add(word)
    return list(dictSet)


# 输入一个list集合，然后返回一个list，在出现过的单词记住1，没出现过的地方初始化0
# 1.num*[0] 初始化的是一组为0的list
# 2.list.index(word)可以返回索引
# 思考：其实可以用dict来做
def inOrOutDict(dictset, input):
    MyValue = len(dictset) * [0]
    for word in input:
        if word in dictset:
            MyValue[dictset.index(word)] = 1
    return MyValue


# 计算p(x|A)和p(A)概率
# 训练模型，使用在之后的预测中
# 防止p()p()p()p()=0,所以初始化都为1，分母初始化成2
def Naive_Bayesian_Model(input, lables):
    numberLenth = len(input)  # 获取有多少条数据
    pA = sum(lables) / numberLenth
    postingList, classVec = loadDataSet()
    dictSet = createDict(postingList)
    sumA = 2.0
    sumB = 2.0
    pAmatrix = np.ones(len(inOrOutDict(dictSet, input[0])))
    pBmatrix = np.ones(len(inOrOutDict(dictSet, input[0])))
    for i in range(numberLenth):
        MyValue = inOrOutDict(dictSet, input[i])
        if lables[i] == 1:
            pAmatrix += MyValue
            sumA += sum(MyValue)
        else:
            pBmatrix += MyValue
            sumB += sum(MyValue)
    pw_A = pAmatrix / sumA
    pw_b = pBmatrix / sumB
    return pw_A, pw_b, pA


def classifyNB(vec2Classify, p0Vec, p1Vec, pClass1):
    """
    使用算法：
        # 将乘法转换为加法
        乘法：P(C|F1F2...Fn) = P(F1F2...Fn|C)P(C)/P(F1F2...Fn)
        加法：P(F1|C)*P(F2|C)....P(Fn|C)P(C) -> log(P(F1|C))+log(P(F2|C))+....+log(P(Fn|C))+log(P(C))
    :param vec2Classify: 待测数据[0,1,1,1,1...]，即要分类的向量
    :param p0Vec: 类别0，即正常文档的[log(P(F1|C0)),log(P(F2|C0)),log(P(F3|C0)),log(P(F4|C0)),log(P(F5|C0))....]列表
    :param p1Vec: 类别1，即侮辱性文档的[log(P(F1|C1)),log(P(F2|C1)),log(P(F3|C1)),log(P(F4|C1)),log(P(F5|C1))....]列表
    :param pClass1: 类别1，侮辱性文件的出现概率
    :return: 类别1 or 0
    """
    # 计算公式  log(P(F1|C))+log(P(F2|C))+....+log(P(Fn|C))+log(P(C))
    # 大家可能会发现，上面的计算公式，没有除以贝叶斯准则的公式的分母，也就是 P(w) （P(w) 指的是此文档在所有的文档中出现的概率）就进行概率大小的比较了，
    # 因为 P(w) 针对的是包含侮辱和非侮辱的全部文档，所以 P(w) 是相同的。
    # 使用 NumPy 数组来计算两个向量相乘的结果，这里的相乘是指对应元素相乘，即先将两个向量中的第一个元素相乘，然后将第2个元素相乘，以此类推。
    # 我的理解是：这里的 vec2Classify * p1Vec 的意思就是将每个词与其对应的概率相关联起来
    p1 = sum(vec2Classify * p1Vec) + np.log(pClass1)  # P(w|c1) * P(c1) ，即贝叶斯准则的分子 p(w=侮辱)=1/2 p(w=没侮辱)=1/2
    p0 = sum(vec2Classify * p0Vec) + np.log(1.0 - pClass1)  # P(w|c0) * P(c0) ，即贝叶斯准则的分子·
    if p1 > p0:
        return 1
    else:
        return 0


if __name__ == "__main__":
    postingList, classVec = loadDataSet()
    dictSet = createDict(postingList)
    pw_A, pw_b, pA = Naive_Bayesian_Model(postingList, classVec)
    testEntry = ['love', 'my', 'dalmation']
    testEntry2 = ['stupid', 'garbage']
    MyValue = inOrOutDict(dictSet, testEntry)
    MyValue2 = inOrOutDict(dictSet, testEntry2)
    print(classifyNB(MyValue, pw_A, pw_b, pA))
    print(classifyNB(MyValue2, pw_A, pw_b, pA))
